#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>

#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include "../include/common_buf.h"
#include "../include/buf_pool.h"
#include "../include/tcp_server.h"
#include "../include/Epoll.h"
#include <ext/hash_map>
#include <unordered_map>
#include "../include/tcp_conn.h"

int tcp_server::_max_con_ = 0;
int tcp_server::_cur_conn = 0;
message_router tcp_server::router_;
pthread_mutex_t tcp_server::_conns_mutex = PTHREAD_MUTEX_INITIALIZER;
std::unordered_map<int,tcp_conn*> tcp_server::conns;
void tcp_server::increase_conn(int confd, tcp_conn *conn)
{
    pthread_mutex_lock(&_conns_mutex);
    conns[confd] = conn;
    _cur_conn++;
    pthread_mutex_unlock(&_conns_mutex);
}

void tcp_server::decrease_conn(int connfd)
{
    pthread_mutex_lock(&_conns_mutex);
    conns.erase(connfd);
    _cur_conn--;
    pthread_mutex_unlock(&_conns_mutex);
}

void tcp_server::get_conn_num(int *cur_conn)
{
    pthread_mutex_lock(&_conns_mutex);
    *cur_conn = _cur_conn;
    pthread_mutex_unlock(&_conns_mutex);
}

void tcp_server::do_accept()
{
    int clientfd = -1;
    fprintf(stderr, "begin to accept\n");
    while (true)
    {
        clientfd = accept(_sockfd, (struct sockaddr *)&_connaddr, &_addrlen);
        if (clientfd == -1)
        {
            // 如果发生了中断
            if (errno == EINTR)
            {
                fprintf(stderr, "accept errno=EINTR\n");
                continue;
            }
            // 如果链接太多
            else if (errno == EMFILE)
            {
                fprintf(stderr, "accept errno=EMFILE\n");
                break;
            }
            // 在非阻塞模式下调用了阻塞操作，在该操作没有完成就返回这个错误
            else if (errno == EAGAIN)
            {
                fprintf(stderr, "accept errno=EAGAIN\n");
                exit(1);
            }
            else
            {
                fprintf(stderr, "accept failed\n");
                exit(1);
            }
        }
        else
        {
            // 添加一些保活机制
            // 向服务器发送消息
            int conn_num = 0;
            get_conn_num(&conn_num);
            if (conn_num >= _max_con_)
            {
                fprintf(stderr,"so many conn! No resoure to make new conn,max_con=%d\n",_max_con_);
                close(clientfd);
                break;
            }
            else
            {
                tcp_conn *conn = new tcp_conn(clientfd, _loop);
                if (conn == nullptr)
                {
                    fprintf(stderr, "new tcp_conn error!\n");
                    exit(-1);
                }
                fprintf(stderr, "new tcp_conn suc , accept ip:%s,port:%d\n", inet_ntoa(_connaddr.sin_addr), _connaddr.sin_port);
            }

            //这里需要直接break掉，否则整个程序会在该位置被阻塞掉
            break;
        }
    }
}

// listen fd 客户端有新链接请求过来的回调函数
void accept_callback(event_loop *loop, int fd, void *args)
{
    tcp_server *server = (tcp_server *)args;
    server->do_accept();
    fprintf(stdout, "accept_callback\n");
}

tcp_server::tcp_server(event_loop *loop, const char *ip, uint16_t port)
{
    bzero(&_connaddr, sizeof(_connaddr));
    // 忽略掉一些信号
    // SIGPIPE:如果客户端关闭，服务端再次write就会产生
    if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
    {
        fprintf(stderr, "signal ignore sigpipe\n");
    }
    // SIGHUP:如果terminal关闭，会给当前进程发送该信号
    if (signal(SIGHUP, SIG_IGN) == SIG_ERR)
    {
        fprintf(stderr, "signal ignore sighup\n");
    }
    // 创建listen套接字
    _sockfd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
    if (_sockfd == -1)
    {
        fprintf(stderr, "tcp_server:socket create err!\n");
        exit(-1);
    }

    // 初始化地址
    struct sockaddr_in server_addr;
    bzero(&server_addr, sizeof(_connaddr));
    server_addr.sin_family = AF_INET;
    inet_aton(ip, &server_addr.sin_addr);
    server_addr.sin_port = htons(port);
    _max_con_ = MAX_CON;
    int op = 1;
    // 设置为不等待，套接字可立即被使用
    if (setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, &op, sizeof(op)) < 0)
    {
        fprintf(stderr, "tcp_server::setsockopt err!\n");
    }
    if (bind(_sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
    {
        fprintf(stderr, "tpc_server::setsockopt err!\n");
    }
    if (listen(_sockfd, 128) == -1)
    {
        fprintf(stderr, "tcp_server::listen err\n");
    }
    _loop = loop;
    _loop->add_io_event(_sockfd, accept_callback, EPOLLIN, this);
    fprintf(stderr, "create tcp_server success!\n");
}

tcp_server::~tcp_server()
{
    close(_sockfd);
}